﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace InputHandlers.KeyboardHandling
{
    /// <summary>
    /// A class to manage tracking of extended keys Alt, Control and Shift.
    /// </summary>
    public class ExtendedKeysExpectationManager
    {
        /// <summary>
        /// The actions to take when found a extended expeced key invoked.
        /// </summary>
        private Action m_countAltAction;
        private Action m_countControlAction;
        private Action m_countShiftAction;
        private Action m_countWinAction;

        /// <summary>
        /// The currently configured expected extended keys.
        /// </summary>
        private Keys m_extendedKeysExpected = Keys.None;

        /// <summary>
        /// The matching list of expected  keys.
        /// </summary>
        private List<ToggleKey> m_toggleKeys = new List<ToggleKey>();

        /// <summary>
        /// Initializes a new instance of the <see cref="CryptoQrUtilities.ExtendedKeysExpectationManagerlly setting an expectation key set.
        /// </summary>
        /// <param name="keyExpectationSet">Key expectation set.</param>
        public ExtendedKeysExpectationManager(Keys keyExpectationSet = Keys.None)
        {
            ResetExpectationResult();

            m_countAltAction = new Action (() => ExtendedKeysLastInvoked |= Keys.Alt);
            m_countControlAction = new Action (() => ExtendedKeysLastInvoked |= Keys.Control);
            m_countShiftAction = new Action (() => ExtendedKeysLastInvoked |= Keys.Shift);
            m_countWinAction = new Action(() => ExtendedKeysLastInvoked |= Keys.Sleep);

            ExpectationSet = keyExpectationSet;
        }

        /// <summary>
        /// Gets the combination of the Alt, Control and Shift keys currently (lately) used.
        /// Note: When setting a key expectation set it clears the old set.
        /// </summary>
        /// <value>The extendeds last invoked.</value>
        public Keys ExtendedKeysLastInvoked { get; private set; }

        /// <summary>
        /// Gets or sets the extended keys expectation set.
        /// An expectation is immutable. Expectation set cannot be reversed. Create another Expectation Manager instead.
        /// </summary>
        public Keys ExpectationSet
        {
            get
            {
                return m_extendedKeysExpected;
            }

            set
            {
                m_extendedKeysExpected = Keys.None;
                m_toggleKeys.Clear();

                if (value.HasFlag(Keys.Alt))
                {
                    ExpectAltKey();
                }

                if (value.HasFlag(Keys.Control))
                {
                    ExpectControlKey();
                }

                if (value.HasFlag(Keys.Shift))
                {
                    ExpectShiftKey();
                }

                if (((short)value & 0xff) == (short)Keys.Sleep)
                {
                    ExpectWinKey();
                }
            }
        }

        /// <summary>
        /// Gets the extended (Alt, Control, Shift) keys state, as a result of a given input key combination.
        /// </summary>
        /// <returns>The state of the extended keys.</returns>
        /// <param name="inputKeys">The keys pressed/released.</param>
        /// <param name="toggleState">Whether the given keys are pressed.</param>
        public Keys UpdateAndGetExtendedKeys(Keys inputKeys, bool toggleState)
        {
            ResetExpectationResult();

            foreach (var keyDefinition in m_toggleKeys)
            {
                keyDefinition.ToggleState(inputKeys, toggleState);
            }

            return ExtendedKeysLastInvoked;
        }

        private void ExpectAltKey()
        {
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.LMenu, m_countAltAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.RMenu, m_countAltAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.Alt, m_countAltAction));

            m_extendedKeysExpected |= Keys.Alt;
        }

        private void ExpectControlKey()
        {
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.LControlKey, m_countControlAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.RControlKey, m_countControlAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.Control, m_countControlAction));

            m_extendedKeysExpected |= Keys.Control;
        }

        private void ExpectShiftKey()
        {
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.LShiftKey, m_countShiftAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.RShiftKey, m_countShiftAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.Shift, m_countShiftAction));

            m_extendedKeysExpected |= Keys.Shift;
        }

        private void ExpectWinKey()
        {
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.RWin, m_countWinAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.LWin, m_countWinAction));
            m_toggleKeys.Add(ToggleKeyFactory.CreateToggleKey(Keys.Sleep, m_countWinAction));
        }

        private void ResetExpectationResult()
        {
            ExtendedKeysLastInvoked = Keys.None;
        }
    }
}

